#pragma once

#include <ctime>
#include <functional>
#include "NetPacket.h"
#include "async/AsyncTaskOwner.h"

#define DEF_RPC_TIMEOUT (60)

class RPCManager;

enum RPCError {
    RPCErrorNone,
    RPCErrorTimeout,
    RPCErrorInterrupt,
    RPCErrorCancel,
    RPCErrorFailed,
};

class RPCActor
{
    friend class RPCManager;
public:
    RPCActor();
    ~RPCActor();

    uint64 RPCInvoke(const INetPacket &pck,
        const std::function<void(INetStream&, int32, bool)> &cb = nullptr,
        AsyncTaskOwner *owner = nullptr, time_t timeout = DEF_RPC_TIMEOUT);
    uint64 RPCTransInvoke(const INetPacket &trans, const INetPacket &pck,
        const std::function<void(INetStream&, int32, bool)> &cb = nullptr,
        AsyncTaskOwner *owner = nullptr, time_t timeout = DEF_RPC_TIMEOUT);

    void RPCReply(const INetPacket &pck,
        uint64 sn, int32 err = RPCErrorNone, bool eof = true);
    void RPCTransReply(const INetPacket &trans, const INetPacket &pck,
        uint64 sn, int32 err = RPCErrorNone, bool eof = true);

    void RPCInterrupt(uint64 sn);

    struct RequestMetaInfo {
        uint64 sn;
    };
    struct ReplyMetaInfo {
        uint64 sn;
        int32 err;
        bool eof;
    };
    static RequestMetaInfo ReadRequestMetaInfo(INetPacket &pck);
    static ReplyMetaInfo ReadReplyMetaInfo(INetPacket &pck);

protected:
    virtual void PushRPCPacket(const INetPacket &trans,
        const INetPacket &pck, const std::string_view &args) = 0;

    inline RPCManager *GetCacheRPCManager() {
        return mgr_ != nullptr ? mgr_ : mgr_ = GetRPCManager();
    }

    void SetReady(bool value) { is_ready_ = value; }
    bool IsReady() const { return is_ready_; }

private:
    virtual RPCManager *GetRPCManager() = 0;

    RPCManager *mgr_;
    bool is_ready_;
};
